#pragma once
#include <iostream>
#include <memory>
#include <ctime>
#include <unistd.h>
#include <sys/types.h>
#include <filesystem>
#include <fstream>
// #include <sys/types.h>
#include <sys/wait.h>
//#include <unistd.h>
#include <cstdio>
#include <fcntl.h>
#include "util.hpp"
#include "log.hpp"

namespace CoR
{
    using namespace ns_util;
#define SIZE 1024

    ////////////////////////////////////责任链模式的处理日志信息////////////////////////////
    // 任务一：当文件的内容达到一定程度时（比如发送信息超过1000条行数，我们就备份一个文件）
    // 任务二：我们将备份的文件进行打包，删除原文件
    class HandlerFile // 责任链基类
    {
    public:
        void Enable() { _enable = true; }
        void Disable() { _enable = false; }
        void SetNext(const std::shared_ptr<HandlerFile> &next) { _next = next; }
        virtual void Execute(const std::string &text) = 0; // 接口
        HandlerFile(const std::string &filepath, const std::string &filename)
            :_filepath(filepath), _filename(filename)
        {}
    protected:
        std::shared_ptr<HandlerFile> _next = nullptr; // 指向下一个任务
        bool _enable = true;                          // 是否启动改任务点
        std::string _filepath;
        std::string _filename;
    };


    // 任务一：备份（超过一定行数的日志内容）
    // 在Linux中一般的备份不是将内容拷贝一份，而是直接重命名
    // 原因：重命名 mv是原子操作
    // 原名称："[level].txt" —> "[level].txt.时间戳"
    const int defaultfmaxline = 1000;
    class HandlerBackup : public HandlerFile
    {
    private:
        int _maxline = defaultfmaxline;

    public:
        HandlerBackup(const std::string &filepath, const std::string &filename)
            : HandlerFile(filepath, filename)
        {
        }
        // _filename: [level].txt
        // _filepath: ../Log/
        virtual void Execute(const std::string &content) override
        {
            // 从第一任务开始处理的重点在于文件
            std::string suffix = std::to_string(time(nullptr)); 
            // 我们需要将处理的文件后缀传给后面的任务
            // 这里就保存起来了
            if (_enable)
            {
                // 先判断是否超过了限制范围
                //std::cout << "check range of file..." << std::endl;
                std::string file = _filepath + _filename; // file: ../Log/level.txt
                //std::cout << file << std::endl;
                if (isOutofRange(file))
                {
                    // 就进行备份
                    //std::cout << "file is out of range, start to backup..." << std::endl;
                    std::string backup_file = file + '.' + suffix; // 备份的文件路径 + 名称
                    // TODO

                    std::filesystem::rename(file, backup_file); // 备份 —> 改文件名
                    //重新开一个文件

                    //std::cout << "backup file done..." << std::endl;

                    // 只有完成了备份才会解决是否进行打包
                    if (_next) // 判断下一个任务节点是否存在
                    {
                        _next->Execute(suffix);
                    }
                    else
                    {
                        //std::cout << "Chain Of Responsibility Done..." << std::endl;
                    }
                }
                //std::cout << "range of file is OK!" << std::endl;
            }
        }

    private:
        bool isOutofRange(const std::string &file)
        {
            std::ifstream ifs(file); // 读取文件
            if (!ifs.is_open())
            {
                std::cerr << "check file fail... because open file fail..." << std::endl;
                return false;
            }
            int lines = 0;
            std::string line;
            while (std::getline(ifs, line))
            {
                ++lines;
            }
            //std::cout << lines << std::endl;
            ifs.close();
            return lines > _maxline;
        }
    };

    // 任务二: 将备份的文件进行打包 并且 删除原来的文件
    // 打包原来的文件：需要进行系统调用：exec*
    class HandlerPackage : public HandlerFile
    {
    public:
        HandlerPackage(const std::string &filepath, const std::string &filename)
            : HandlerFile(filepath, filename)
        {
        }

    public:
        virtual void Execute(const std::string &suffix) override
        {
            if (_enable)
            {
                std::string src_filename = _filename + '.' + suffix; // 源文件名 [level].txt.时间戳
                std::string backup_file = _filepath + src_filename;  // 备份的文件路径 + 名称
                std::string tar_filename = src_filename + ".tgz";    // 打包文件名
                // 打包任务交给子进程
                pid_t pid = fork();
                if (pid == 0)
                {
                    // child
                    // 对文件进行打包
                    //  ../Log/[level].txt.时间戳 —> ../Log/[level].txt.时间戳.tgz

                    // 1、更改工作路径：打包一个文件需要在当前文件的目录中才可以完成我们想要的打包
                    std::filesystem::current_path(_filepath);
                    // 2、程序替换，exec*
                    execlp("tar", "tar", "-czf", tar_filename.c_str(), src_filename.c_str(), nullptr);

                    // 如果走到这里，说明程序替换失败 —> 原因：程序替换之后代码不会向后执行
                    exit(1);
                }
                // parent
                int status = 0;
                pid_t rid = waitpid(pid, &status, 0);
                if (rid > 0)
                {
                    if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
                    {
                        // 打包成功，删除源文件内容 —> 需要带路径
                        //std::cout << "step 4: package file done..." << std::endl;
                        std::filesystem::remove(backup_file);
                        //需要重写创建一个文件
                        std::string file = _filepath + _filename;
                        std::ofstream ofs(file);
                        ofs.close();
                    }
                }
            }
            // 判断下一个任务节点是否存在
            if (_next)
            {
                _next->Execute(suffix);
            }
            else
            {
                //std::cout << "Chain Of Responsibility Done..." << std::endl;
            }
        }
    };

    const std::string defaultfilepath = "../Log/";
    const std::string defaultfilename = "level.txt"; 
    // 入口类
    class Enter
    {
    public:
    // 路径 + 文件名构造的时候就传入了 "../comm/Log/" "[level].txt"
        Enter(const std::string &filepath = defaultfilepath, const std::string &filename = defaultfilename)
        {
            // 指向对象：make_shared就是new对象 + 赋值给指针
            _backup = std::make_shared<HandlerBackup>(filepath, filename);
            _package = std::make_shared<HandlerPackage>(filepath, filename);

            // 确定责任链
            _backup->SetNext(_package);
        }
        void Able(bool isformat, bool issave, bool isbackup, bool ispackage)
        {
            isformat ? _backup->Enable() : _backup->Disable();
            isformat ? _package->Enable() : _package->Disable();
        }
        void Run(const std::string &content = "")
        {
            _backup->Execute(content);
        }

        private:
        std::shared_ptr<HandlerFile> _backup;  // 任务一：备份
        std::shared_ptr<HandlerFile> _package; // 任务二：打包
    };
}